﻿#if ENABLE_HCLR

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using HybridCLR;
using UnityEngine;

namespace Core
{
    public interface IHclrHelper : IHelperTag
    {
        ETTask<bool> DecompressMetadatas();
        void LoadMetadatas();
        void LoadDlls(List<string> dllPaths);
    }

    public class HclrHelper : Helper, IHclrHelper
    {
        private IAssetHelper assetHelper;
        private HashSet<string> metadataNames = new HashSet<string>();

        public string hclrStreamingRoot;
        public string hclrPersistentRoot;
        public string metadatConfigSavePath;

        public override void OnInit()
        {
            hclrStreamingRoot = $"{Application.streamingAssetsPath}/{GameConst.HCLR_METADATA_PATH}";
            hclrPersistentRoot = $"{Application.persistentDataPath}/{GameConst.HCLR_METADATA_PATH}";
            metadatConfigSavePath = $"{hclrPersistentRoot}/{GameConst.HCLR_METADATA_CONFIG_NAME}";

            assetHelper = GetHelper<IAssetHelper>();
        }

        public async ETTask<bool> DecompressMetadatas()
        {
            var path = $"{hclrStreamingRoot}/{GameConst.HCLR_METADATA_CONFIG_NAME}";

            var config = await assetHelper.LoadStringAsync(path);
            if (string.IsNullOrEmpty(config))
            {
                Debug.LogError($"Can not get hclr metadata content:{path}");
                return false;
            }

            ParseMetadataConfig(config);

            foreach (var metadataName in metadataNames)
            {
                var savePath = $"{hclrPersistentRoot}/{metadataName}";

                // 从steamingAsset中加载
                var streamingFilePath = $"{hclrStreamingRoot}/{metadataName}";
                var fileBytes = await assetHelper.LoadBytesAsync(streamingFilePath);
                if (fileBytes == null)
                {
                    Debug.LogError($"{streamingFilePath} does not exist.");
                    continue;
                }

                // 解压
                fileBytes = await Utils.DecompressAsync(fileBytes);

                // 保存
                await assetHelper.SaveBytesAsync(savePath, fileBytes);
            }

            await assetHelper.SaveStringAsync(metadatConfigSavePath, config);

            return true;
        }
        
        private void ParseMetadataConfig(string content)
        {
            var lines = content.Split(Environment.NewLine);
            foreach (var line in lines)
            {
                var metadataName = line.Trim();
                if (string.IsNullOrEmpty(metadataName))
                    continue;
                metadataNames.Add(metadataName);
            }
        }

        public void LoadDlls(List<string> dllPaths)
        {
            var currentCount = 0;
            foreach (var path in dllPaths)
            {
                var bytes = assetHelper.LoadBytes(path);
                Assembly.Load(bytes);
                new OnLoadHclrDllProgress(currentCount, dllPaths.Count).Publish();
            }

            new OnLoadHclrDllComplete().Publish();
        }

        public void LoadMetadatas()
        {
            var config = assetHelper.LoadString(metadatConfigSavePath);
            if (string.IsNullOrEmpty(config))
            {
                Debug.LogError($"Can not get hclr metadata content:{metadatConfigSavePath}");
                return;
            }

            ParseMetadataConfig(config);

            var currentCount = 0;
            foreach (var metadataName in metadataNames)
            {
                var path = $"{hclrPersistentRoot}/{metadataName}";
                var bytes = assetHelper.LoadBytes(path);
                var code = HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(bytes, HomologousImageMode.SuperSet);
                if (code != LoadImageErrorCode.OK)
                {
                    Debug.LogError($"load metadata error:{code}");
                    return;
                }

                new OnLoadHclrMetadataProgress(currentCount, metadataNames.Count).Publish();
            }

            new OnLoadHclrMetadataComplete().Publish();
        }
    }

    public struct LoadHclrMetadatas : IEventData { }

    public struct OnLoadHclrMetadataProgress : IEventData 
    {
        public int currentCount;
        public int totalCount;

        public OnLoadHclrMetadataProgress(int currentCount, int totalCount)
        {
            this.currentCount = currentCount;
            this.totalCount = totalCount;
        }
    }

    public struct OnLoadHclrMetadataComplete : IEventData { }

    public struct LoadHclrDlls : IEventData { }

    public struct OnLoadHclrDllProgress : IEventData
    {
        public int currentCount;
        public int totalCount;

        public OnLoadHclrDllProgress(int currentCount, int totalCount)
        {
            this.currentCount = currentCount;
            this.totalCount = totalCount;
        }
    }

    public struct OnLoadHclrDllComplete : IEventData { }
}

#endif